package com.iretailer.report.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.iretailer.report.dto.CameraFlowModel;
import com.iretailer.report.influxdb.model.*;
import com.iretailer.report.influxdb.service.InfluxdbDwellService;
import com.iretailer.report.influxdb.service.InfluxdbEasAlarmService;
import com.iretailer.report.influxdb.service.InfluxdbFlowService;
import com.iretailer.report.influxdb.service.InfluxdbPassByService;
import com.iretailer.report.influxdb.util.TableName;
import com.iretailer.report.model.*;
import com.iretailer.report.quartz.EasAlarmSummary;
import com.iretailer.report.util.TimeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.MessageFormat;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @Auther: xjw
 * @Date: 2019/3/11 17:49
 * @Description:
 */
@Service
public class CollectService {
    private static final Logger log = LoggerFactory.getLogger(CollectService.class);
    private static final ExecutorService pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);

    @Autowired
    private PhysicalzonesService physicalzonesService;
    @Autowired
    private InfluxdbFlowService influxdbFlowService;
    @Autowired
    private InfluxdbDwellService influxdbDwellService;
    @Autowired
    private BusinessTimeService businessTimeService;
    @Autowired
    private CategoriesService categoriesService;
    @Autowired
    private InfluxdbPassByService influxdbPassByService;
    @Autowired
    private DevicesService devicesService;
    @Autowired
    private InfluxdbEasAlarmService influxdbEasAlarmService;
    @Autowired
    private EasAlarmSummary easAlarmSummary;

    public void resetBusiness(List<Malls> malls, List<String> dates) {
        for (Malls m : malls) {
            pool.execute(() -> {
                long start = System.currentTimeMillis();
                List<PhysicalZoneMall> pzms = physicalzonesService.selectByEnabled_MallId(1, m.getId());
                if (pzms != null && pzms.size() > 0) {
                    // 查询出 物理区域
                    List<PhysicalZoneMall> physicalZoneCameras = physicalzonesService.setCmaeraZones(pzms);
                    Set<Long> zoneIds = new HashSet<>();
                    for (PhysicalZoneMall p : physicalZoneCameras) {
                        if (p.getCameraZoneIds() != null && p.getCameraZoneIds().size() > 0) {
                            zoneIds.addAll(p.getCameraZoneIds());
                        }
                    }
                    // 查询出eas设备
                    List<DeviceType> easDevices = devicesService.selectDeviceType(new QueryWrapper<>().eq("MallId", m.getId()).eq("DeviceTypeName", "Eas"));
                    List<Long> easZoneIds = easDevices.stream().map(DeviceType::getId).collect(Collectors.toList());

                    Map<String, MallBusinessTime> bmap = businessTimeService.filterBusinessTime(m.getId(), dates);
                    for (String date : dates) {
                        MallBusinessTime b = bmap.get(date);
                        if (b != null && b.getStartTime() != null && b.getEndTime() != null) {
                            String startTime = MessageFormat.format("{0} {1}", date, b.getStartTime());
                            String endTime = MessageFormat.format("{0} {1}", date, b.getEndTime());
                            // 客流和passBy数据重新汇总
                            //置零
                            String zeroStartTime = TimeUtil.getStartTime(date);
                            String zeroEndTime = TimeUtil.getEndTime(date);
                            List<Passengerflow> zero = influxdbFlowService.getZero(m.getTimeZone(), TableName.Passengerflow.Passengerflow_B, new ArrayList<>(zoneIds), zeroStartTime, zeroEndTime);
                            List<Passengerflow> flow = influxdbFlowService.selectFlowGrid(m.getTimeZone(), TableName.Passengerflow.Passengerflow, new ArrayList<>(zoneIds), startTime, endTime);
                            Map<String, Passengerflow> flowMap = flow.stream().collect(Collectors.toMap(Passengerflow::getGroup, a -> a));
                            for (Passengerflow p : zero) {
                                String key = MessageFormat.format("{0}_{1}", p.getZoneid(), p.getTime());
                                Passengerflow f = flowMap.get(key);
                                if (f == null) {
                                    flow.add(p);
                                }
                            }
                            influxdbFlowService.insertPassengerflow(flow, TableName.Passengerflow.Passengerflow_B);

                            // dwelltime 数据重置
                            List<DwellTime> zeroDwellTime = influxdbDwellService.getZero(m.getTimeZone(), TableName.Dwell.DwellTime_B, new ArrayList<>(zoneIds), zeroStartTime, zeroEndTime);
                            List<DwellTime> dwellTimes = influxdbDwellService.selectDwellGrid(m.getTimeZone(), TableName.Dwell.DwellTime, new ArrayList<>(zoneIds), startTime, endTime);
                            Map<String, DwellTime> dwellTimeMap = dwellTimes.stream().collect(Collectors.toMap(DwellTime::getGroup, a -> a));
                            for (DwellTime d : zeroDwellTime) {
                                String key = MessageFormat.format("{0}_{1}", d.getZoneid(), d.getTime());
                                DwellTime dt = dwellTimeMap.get(key);
                                if (dt == null) {
                                    dwellTimes.add(dt);
                                }
                            }
                            influxdbDwellService.insertDwellTime(dwellTimes, TableName.Dwell.DwellTime_B);

                            // eas 报警重置
                            List<EasAlarm> zeroEasAlarms = influxdbEasAlarmService.getZero(m.getTimeZone(), TableName.EasAlarm.EasAlarm_B, easZoneIds, zeroStartTime, zeroEndTime);
                            List<EasAlarm> easAlarms = influxdbEasAlarmService.selectEasAlarmGrid(m.getTimeZone(), TableName.EasAlarm.EasAlarm, easZoneIds, startTime, endTime);
                            Map<String, EasAlarm> easMap = easAlarms.stream().collect(Collectors.toMap(EasAlarm::getGroup, a -> a));
                            for (EasAlarm e : zeroEasAlarms) {
                                String key = String.format("%s_%s", e.getZoneid(), e.getTime());
                                EasAlarm easAlarm = easMap.get(key);
                                if (easAlarm == null) {
                                    easAlarms.add(e);
                                }
                            }
                            influxdbEasAlarmService.insertEasAlarm(easAlarms, TableName.EasAlarm.EasAlarm_B);


                        }
                        log.error("------reset businessTime {} {}", m.getName(), date);
                    }
                }
                long end = System.currentTimeMillis();
                log.error("------reset businessTime {} time:{}", m.getName(), (end - start));
            });
        }
    }

    public void collect(List<Malls> malls, List<String> dates) {
        if (malls != null && malls.size() > 0) {
            log.error("---------- dates:{} {}", dates.get(0), dates.get(dates.size() - 1));
            List<Categories> zoneTypes = categoriesService.findChildrenCategoriesByKeyName("ZoneTypes");
            //查出所有Mall
            for (int i = 0; i < malls.size(); i++) {
                final int ii = i;
                pool.execute(() -> {
                    long start = System.currentTimeMillis();
                    Malls m = malls.get(ii);
                    Categories eas = categoriesService.selectCategoryByKeyName("Eas");
                    Wrapper<DeviceMall> wrapper = new QueryWrapper<DeviceMall>().lambda()
                            .eq(DeviceMall::getDeviceType, eas.getId())
                            .eq(DeviceMall::getMallId, m.getId());
                    List<DeviceMall> deviceMalls = devicesService.selectDeviceMall(wrapper);
                    List<String> deviceIds = deviceMalls.stream().map(mall->mall.getId().toString()).collect(Collectors.toList());
                    easAlarmSummary.summary(dates, deviceIds);
                    runFlow(zoneTypes, m, dates);
                    long end = System.currentTimeMillis();
                    log.error("------index:{}  mallName: {} time:{}", ii, m.getName(), (end - start));
                });
            }
        }
    }

    private class DataKeMap {
        List<Long> phyzoneIds = new ArrayList<>();
        Set<Long> zoneId = new HashSet<>();
        List<PhysicalZoneMall> physicalZoneMalls = new ArrayList<>();

        public List<Long> getPhyzoneIds() {
            return phyzoneIds;
        }

        public List<PhysicalZoneMall> getPhysicalZoneMalls() {
            return physicalZoneMalls;
        }

        public Set<Long> getZoneId() {
            return zoneId;
        }
    }

    public void runFlow(List<Categories> zoneTypes, Malls m, List<String> dates) {

        Map<String, String> passbyKeyMap = new HashMap<>();
        Map<String, String> dwellKeyMap = new HashMap<>();
        Map<String, String> flowKeyMap = new HashMap<>();
        for (Categories c : zoneTypes) {
            switch (c.getKeyName()) {
                case "MallShop":
                    passbyKeyMap.put(c.getId(), c.getKeyName());
                    break;
                case "Shelf":
                    dwellKeyMap.put(c.getId(), c.getKeyName());
                    break;
                default:
                    flowKeyMap.put(c.getId(), c.getKeyName());
                    break;
            }
        }

        List<PhysicalZoneMall> pzms = physicalzonesService.selectByEnabled_MallId(1, m.getId());
        if (pzms != null && pzms.size() > 0) {
            DataKeMap fdkm = new DataKeMap();
            DataKeMap pdkm = new DataKeMap();
            DataKeMap dwellDkm = new DataKeMap();
            List<PhysicalZoneMall> physicalZoneCameras = physicalzonesService.setCmaeraZones(pzms);
            for (PhysicalZoneMall p : physicalZoneCameras) {
                if (flowKeyMap.get(p.getZoneTypeId()) != null) {
                    fdkm.getPhyzoneIds().add(p.getId());
                    if (p.getCameraZoneIds() != null && p.getCameraZoneIds().size() > 0) {
                        fdkm.getPhysicalZoneMalls().add(p);
                        fdkm.getZoneId().addAll(p.getCameraZoneIds());
                    }
                } else if (passbyKeyMap.get(p.getZoneTypeId()) != null) {
                    pdkm.getPhysicalZoneMalls().add(p);
                    pdkm.getPhyzoneIds().add(p.getId());
                } else if (dwellKeyMap.get(p.getZoneTypeId()) != null) {
                    dwellDkm.getPhysicalZoneMalls().add(p);
                    dwellDkm.getPhyzoneIds().add(p.getId());
                }
            }
            for (String date : dates) {
                String startTime = TimeUtil.getStartTime(date);
                String endTime = TimeUtil.getEndTime(date);
                // flow
                if (fdkm.getPhyzoneIds().size() > 0) {
                    //客流置零
                    List<Passengerflow> zeroP = influxdbFlowService.getZero(m.getTimeZone(), TableName.Passengerflow.Passengerflow_PZ, fdkm.getPhyzoneIds(), startTime, endTime);
                    //客流汇总
                    List<CameraFlowModel> flow = influxdbFlowService.getPhysicalZones_PeriodFlow(m.getTimeZone(), fdkm.getPhysicalZoneMalls(), fdkm.getZoneId(), startTime, endTime, "5m");
                    if (flow != null && flow.size() > 0) {
                        List<Passengerflow> postFlow = new ArrayList<>();
                        for (CameraFlowModel cf : flow) {
                            Passengerflow p = new Passengerflow();
                            p.setTime(cf.getZonedDateTime());
                            p.setZoneid(cf.getPhysicalZoneId());
                            p.setCountExit(cf.getCountexit());
                            p.setCountEnter(cf.getCountenter());
                            postFlow.add(p);
                        }
                        Map<String, Passengerflow> postwMap = postFlow.stream().collect(Collectors.toMap(Passengerflow::getGroup, a -> a));
                        for (Passengerflow p : zeroP) {
                            String key = MessageFormat.format("{0}_{1}", p.getZoneid(), p.getTime());
                            Passengerflow f = postwMap.get(key);
                            if (f == null) {
                                postFlow.add(p);
                            }
                        }
//                    log.error("---flow {} {}", m.getName(), post.size());
                        influxdbFlowService.insertPassengerflow(postFlow, TableName.Passengerflow.Passengerflow_PZ);
                    }
                }
                //passby 汇总
                if (pdkm.getPhysicalZoneMalls().size() > 0) {
                    List<PassByFlow> zeroPbf = influxdbPassByService.getZero(m.getTimeZone(), TableName.PassByflow.Passbyflow, pdkm.getPhyzoneIds(), startTime, endTime);
                    List<PassByFlow> postPassByFlow = new ArrayList<>();
                    for (PhysicalZoneMall p : pdkm.getPhysicalZoneMalls()) {
                        List<CameraZoneCamera> cameraZoneCameras = p.getCameraZones();
                        if (cameraZoneCameras != null && cameraZoneCameras.size() > 0) {
                            Map<String, List<CameraZoneCamera>> czcMap = cameraZoneCameras.stream().collect(Collectors.groupingBy(CameraZoneCamera::getZoneClass));
                            List<CameraZoneCamera> countZoneCamera = czcMap.get("Count");
                            List<CameraZoneCamera> passByZoneCamera = czcMap.get("Passby");
                            List<CameraFlowModel> countFlow = new ArrayList<>();
                            if (countZoneCamera != null && countZoneCamera.size() > 0) {
                                Set<Long> zoneIds = countZoneCamera.stream().map(it -> {
                                    return it.getId();
                                }).collect(Collectors.toSet());
                                countFlow.addAll(influxdbFlowService.getPhysicalZones_PeriodFlow(m.getTimeZone(), Arrays.asList(p), zoneIds, startTime, endTime, "5m"));
                            }
                            List<Passengerflow> passflow = new ArrayList<>();
                            if (passByZoneCamera != null && passByZoneCamera.size() > 0) {
                                List<Long> zoneIds = passByZoneCamera.stream().map(it -> {
                                    return it.getId();
                                }).collect(Collectors.toList());
                                passflow.addAll(influxdbFlowService.selectflowGroupByTime(m.getTimeZone(), TableName.Passengerflow.Passengerflow_B, zoneIds, startTime, endTime, "5m"));
                            }
                            Map<ZonedDateTime, PassByFlow> pbfsMap = new HashMap<>(countFlow.size());
                            for (CameraFlowModel c : countFlow) {
                                pbfsMap.put(c.getZonedDateTime(), new PassByFlow(p.getId().toString(), c.getZonedDateTime(), 0, c.getCountenter()));
                            }
                            Map<ZonedDateTime, List<Passengerflow>> passflowMap = passflow.stream().collect(Collectors.groupingBy(Passengerflow::getTime));
                            for (Map.Entry<ZonedDateTime, List<Passengerflow>> entry : passflowMap.entrySet()) {
                                ZonedDateTime time = entry.getKey();
                                int passCount = 0;
                                for (Passengerflow pf : entry.getValue()) {
                                    passCount += pf.getCountEnter();
                                }
                                PassByFlow pbf = pbfsMap.get(time);
                                if (pbf != null) {
                                    pbf.setCountPass(passCount);
                                } else {
                                    pbfsMap.put(time, new PassByFlow(p.getId().toString(), time, passCount, 0));
                                }
                            }
                            postPassByFlow.addAll(pbfsMap.values());
                        }
                    }
                    // zero
                    Map<String, PassByFlow> postMap = postPassByFlow.stream().collect(Collectors.toMap(PassByFlow::getGroup, a -> a));
                    for (PassByFlow pbf : zeroPbf) {
                        String key = MessageFormat.format("{0}_{1}", pbf.getZoneid(), pbf.getTime());
                        PassByFlow pf = postMap.get(key);
                        if (pf == null) {
                            postPassByFlow.add(pbf);
                        }
                    }
                    influxdbPassByService.insertPassBy(postPassByFlow, TableName.PassByflow.Passbyflow);
                }
                if (dwellDkm.getPhysicalZoneMalls().size() > 0) {
                    List<DwellTimePZ> zeroDwell = influxdbDwellService.getDwellTimePZZero(m.getTimeZone(), dwellDkm.getPhyzoneIds(), startTime, endTime);
                    // 查询案场主出入口客流
                    Map<ZonedDateTime, CameraFlowModel> entranceMap = getCollectEntranceFlow(m, startTime, endTime, "5m");

                    List<DwellTimePZ> postDwellTimePZs = new ArrayList<>();
                    for (PhysicalZoneMall p : dwellDkm.getPhysicalZoneMalls()) {
                        List<CameraZoneCamera> cameraZoneCameras = p.getCameraZones();
                        if (cameraZoneCameras != null && cameraZoneCameras.size() > 0) {
                            Map<String, List<CameraZoneCamera>> czcMap = cameraZoneCameras.stream().collect(Collectors.groupingBy(CameraZoneCamera::getZoneClass));
                            List<CameraZoneCamera> passByZoneCamera = czcMap.get("Passby");
                            List<CameraZoneCamera> dwellTimeZoneCamera = czcMap.get("Dwell");
                            // 绘制出 DwellTimePZ
                            Map<ZonedDateTime, DwellTimePZ> dwellTimePZMap = new HashMap<>(dwellKeyMap.keySet().size());

                            for (Map.Entry<ZonedDateTime, CameraFlowModel> entry : entranceMap.entrySet()) {
                                ZonedDateTime time = entry.getKey();
                                DwellTimePZ dpz = new DwellTimePZ(p.getId().toString(), time, entry.getValue().getCountenter(), 0, 0, 0);
                                dwellTimePZMap.put(time, dpz);
                            }
                            // dwell
                            List<DwellTime> dwellTimes = new ArrayList<>();
                            if (dwellTimeZoneCamera != null && dwellTimeZoneCamera.size() > 0) {
                                List<Long> zoneIds = dwellTimeZoneCamera.stream().map(it -> {
                                    return it.getId();
                                }).collect(Collectors.toList());
                                List<DwellTime> list = influxdbDwellService.selectDwellGroupByTime(m.getTimeZone(), TableName.Dwell.DwellTime_B, zoneIds, startTime, endTime, "5m");
                                dwellTimes.addAll(list);
                            }

                            // 路过客流
                            List<Passengerflow> passflow = new ArrayList<>();
                            if (passByZoneCamera != null && passByZoneCamera.size() > 0) {
                                List<Long> zoneIds = passByZoneCamera.stream().map(it -> {
                                    return it.getId();
                                }).collect(Collectors.toList());
                                passflow.addAll(influxdbFlowService.selectflowGroupByTime(m.getTimeZone(), TableName.Passengerflow.Passengerflow_B, zoneIds, startTime, endTime, "5m"));
                            }
                            Map<ZonedDateTime, List<DwellTime>> dwellTimeMap = dwellTimes.stream().collect(Collectors.groupingBy(DwellTime::getTime));
                            // dwell
                            for (Map.Entry<ZonedDateTime, List<DwellTime>> entry : dwellTimeMap.entrySet()) {
                                ZonedDateTime time = entry.getKey();
                                int stayCount = 0;
                                int secondsCount = 0;
                                for (DwellTime d : entry.getValue()) {
                                    stayCount += d.getNumberServed();
                                    secondsCount += d.getSecondsOccupied();
                                }
                                DwellTimePZ dpz = dwellTimePZMap.get(time);
                                if (dpz != null) {
                                    dpz.setStayCount(stayCount);
                                    dpz.setSecondsCount(secondsCount);
                                } else {
                                    DwellTimePZ n_dpz = new DwellTimePZ(p.getId().toString(), time, 0, 0, stayCount, secondsCount);
                                    dwellTimePZMap.put(time, n_dpz);
                                }
                            }
                            Map<ZonedDateTime, List<Passengerflow>> passflowMap = passflow.stream().collect(Collectors.groupingBy(Passengerflow::getTime));
                            // pass
                            for (Map.Entry<ZonedDateTime, List<Passengerflow>> entry : passflowMap.entrySet()) {
                                ZonedDateTime time = entry.getKey();
                                int passCount = 0;
                                for (Passengerflow pf : entry.getValue()) {
                                    passCount += pf.getCountEnter();
                                }
                                DwellTimePZ dpz = dwellTimePZMap.get(time);
                                if (dpz != null) {
                                    dpz.setPassCount(passCount);
                                } else {
                                    DwellTimePZ n_dpz = new DwellTimePZ(p.getId().toString(), time, 0, passCount, 0, 0);
                                    dwellTimePZMap.put(time, n_dpz);
                                }
                            }
                            postDwellTimePZs.addAll(dwellTimePZMap.values());

                        }
                    }
                    // zero
                    Map<String, DwellTimePZ> postMap = postDwellTimePZs.stream().collect(Collectors.toMap(DwellTimePZ::getGroup, a -> a));
                    for (DwellTimePZ dtpz : zeroDwell) {
                        String key = MessageFormat.format("{0}_{1}", dtpz.getZoneid(), dtpz.getTime());
                        DwellTimePZ pf = postMap.get(key);
                        if (pf == null) {
                            postDwellTimePZs.add(dtpz);
                        }
                    }
//                        log.error(JSON.toJSONString(postDwellTimePZs));
                    // insert
                    influxdbDwellService.insertDwellTimePZ(postDwellTimePZs);
                }
            }
        }
    }


    /***
     * 主出入口的数据汇总出来查询
     * @param m
     * @param startTime
     * @param endTime
     * @param period
     * @return
     */
    public Map<ZonedDateTime, CameraFlowModel> getCollectEntranceFlow(Malls m, String startTime, String endTime, String period) {
        Categories categories = categoriesService.selectCategoryByKeyName("Entrance");
        List<PhysicalZoneMall> physicalZone = physicalzonesService.selectByMallIds_ZoneTypeId(Arrays.asList(m.getId()), categories.getId());
        List<CameraFlowModel> flows = influxdbFlowService.getPhysicalZones_PeriodFlow(physicalZone, startTime, endTime, period);
        Map<ZonedDateTime, List<CameraFlowModel>> flowMap = flows.stream().collect(Collectors.groupingBy(CameraFlowModel::getZonedDateTime));
        Map<ZonedDateTime, CameraFlowModel> result = new HashMap<>(flowMap.size());
        for (Map.Entry<ZonedDateTime, List<CameraFlowModel>> entry : flowMap.entrySet()) {
            CameraFlowModel fm = new CameraFlowModel();
            fm.setMallId(m.getId());
            fm.setMallName(m.getName());
            int countEnter = 0;
            int countExit = 0;
            for (CameraFlowModel f : entry.getValue()) {
                countEnter += f.getCountenter();
                countExit += f.getCountexit();
            }
            fm.setCountenter(countEnter);
            fm.setCountexit(countExit);
            result.put(entry.getKey(), fm);
        }
        return result;
    }
}
